<?php

/**
 * Maikuro CMS
 *
 * LICENSE:
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * The license is bundled with this package in the file LICENSE.txt.
 * You may also obtain a copy of the License at this URL:
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * @copyright  Copyright (c) 2010, Jonathan Hedrén
 * @author     Jonathan Hedrén <jonathan.hedren@gmail.com>
 * @license    http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
 */

final class PagesViewController extends AbstractBackendEntityViewController
{
    /**
     *
     * @var PageService
     */
    private $pageService;

    /**
     *
     * @var TemplateService
     */
    private $templateService;

    /**
     * @var string
     */
    protected $permissionSubject = 'page';

    /**
     *
     * @var ICacheHandler
     */
    private $cacheHandler;


    /**
     *
     * @param BackendApplicationContext $applicationContext
     * @param PageService $pageService
     * @param TemplateService $templateService
     */
    public function  __construct(
            BackendApplicationContext $applicationContext,
            PageService $pageService,
            TemplateService $templateService,
            ICacheHandler $cacheHandler) {
        $this->applicationContext = $applicationContext;
        $this->pageService = $pageService;
        $this->templateService = $templateService;
        $this->cacheHandler = $cacheHandler;
    }

    /**
     *
     */
    protected function handleCreateAction()
    {
        $this->request->forward('pages/edit', $this->request->getParams());
    }

    /**
     *
     */
    protected function handleDeleteAction()
    {

        $this->applicationContext->checkSitePermission('page', Constants::ACCESS_LEVEL_VIEW_EDIT_CREATE_DELETE);

        $pageId = (int) $this->request->getParam(1);

        $currentPage = $this->pageService->getPage($pageId, $this->applicationContext->getCurrentSite()->id);

        if ($currentPage == null) {
            $this->request->redirectTo('404');
        }

        if ($this->request->isPost()) {
            if ($this->request->getPost('cancel')) {
                $this->request->redirectTo('pages/');
            }
            try {
                $siteId = $currentPage->siteId;
                
                if ($this->request->getPost('deleteDescendants')) {
                    $this->pageService->delete($currentPage, true);
                } else {
                    $this->pageService->delete($currentPage);
                }

                $this->pageService->purgeCache($siteId);

                View::getMessenger()->addMessage('Page deleted.');
                
                $this->request->redirectTo('pages/');
            } catch (Exception $e) {
                View::getMessenger()
                    ->addMessage('Page NOT deleted: ' . $e->getMessage(), Messenger::ERROR);
                $this->request->redirectTo($this->request->getUri());
            }

        }

        $pageTitle = Translator::translate('Delete page') . " '$currentPage->title'";
        $this->view->setVar('title', $pageTitle)
                   ->setVar('currentPage', $currentPage)
                   ->setName('pages/delete')
                   ->render();
    }

    private function handlePageTreeExtractionAndContraction($siteId)
    {
        $expandPageId = $this->request->getParam('expand');
        if ($expandPageId !== null) {
            $page = $this->pageService->getPage($expandPageId, $siteId);
            if ($page !== null) {
                $pageTreeMeta = Session::get('__page_tree_meta');
                $pageTreeMeta[$page->id] = true;
                $expandedChildren = $this->pageService->getChildrenFor($page);
                foreach ($expandedChildren as $child) {
                    $pageTreeMeta[$child->id] = $this->pageService->hasPageChildren($child->id) ? false : null;
                }
                Session::set('__page_tree_meta', $pageTreeMeta);
                $this->cacheHandler->clean('backend-pages-' . $siteId . $this->applicationContext->getCurrentAdministrator()->id);
                $this->request->redirectTo('pages');
            }
        }

        $contractPageId = $this->request->getParam('contract');
        if ($contractPageId !== null) {
            $page = $this->pageService->getPage($contractPageId, $siteId);
            if ($page !== null) {
                $pageTreeMeta = Session::get('__page_tree_meta');
                $pageTreeMeta[$page->id] = false;
                $contractedChildren = $this->pageService->getChildrenFor($page);
                foreach ($contractedChildren as $child) {
                    if (isset($pageTreeMeta[$child->id])) {
                        unset($pageTreeMeta[$child->id]);
                    }
                }
                Session::set('__page_tree_meta', $pageTreeMeta);
                $this->cacheHandler->clean('backend-pages-' . $siteId . Session::getId());
                $this->request->redirectTo('pages');
            }
        }
    }

    /**
     *
     * @param Request $request
     * @param Response $response
     * @param View $view
     */
    public function handleRequest(Request $request, Response $response, View $view)
    {
        $this->applicationContext->checkSitePermission('page', Constants::ACCESS_LEVEL_VIEW);

        $siteId = $this->applicationContext->getCurrentSite()->id;

        $this->request = $request;
        $this->response = $response;
        $this->view = $view;

        $this->handlePageTreeExtractionAndContraction($siteId);

        $sessionUser = $this->applicationContext->getSessionUser();
        
        $hasEditPermission = $sessionUser->hasPermission($this->permissionSubject, Constants::ACCESS_LEVEL_VIEW_EDIT, null, $siteId);
        $hasCreatePermission = $sessionUser->hasPermission($this->permissionSubject, Constants::ACCESS_LEVEL_VIEW_EDIT_CREATE, null, $siteId);
        $hasDeletePermission = $sessionUser->hasPermission($this->permissionSubject, Constants::ACCESS_LEVEL_VIEW_EDIT_CREATE_DELETE, null, $siteId);

        $viewModel = new ViewModel($this->applicationContext->getMenu());
        $this->applicationContext->getEventHandler()->dispatchEvent(Constants::EVENT_VIEW_PRE_RENDER, $viewModel);

        $view->setVar('hasEditPermission', $hasEditPermission)
             ->setVar('hasCreatePermission', $hasCreatePermission)
             ->setVar('hasDeletePermission', $hasDeletePermission)
             ->setVar('currentSiteName', $this->applicationContext->getCurrentSite()->name)
             ->setVar('currentAdministrator', $this->applicationContext->getCurrentAdministrator())
             ->setVar('viewModel', $viewModel)
             ->setLayout('content');

        $contentMenu = $this->applicationContext->getMenu()->getMenuItem('content');

        if ($contentMenu != null) {
            $contentMenu->setActive();

            $subMenuItems = $contentMenu->getSubMenu()->getMenuItems();
            $pagesMenuItem = $subMenuItems['site' . $siteId]->setActive()->getSubMenu()->getMenuItem('pages');
            $pagesMenuItem->setActive();
        }

        $this->switchAction($request);
    }

    /**
     *
     */
    protected function handleEditAction()
    {
        if ($this->request->isPost() && $this->request->getPost('cancel')) {
            $this->request->redirectTo('pages/');
        }

        $pageId = (int) $this->request->getParam(1);

        $this->applicationContext->checkSitePermission('page', Constants::ACCESS_LEVEL_VIEW_EDIT);

        $currentSite = $this->applicationContext->getCurrentSite();

        $page = $this->pageService->getPage($pageId, $currentSite->id);

        if ($page == null) {
            $this->applicationContext->checkSitePermission('page', Constants::ACCESS_LEVEL_VIEW_EDIT_CREATE);

            $pageTitle = Translator::translate('Create new page');

            $page = $this->pageService->create();
            $date = new DateTime();
            $page->created = $date->format(DateTime::W3C);

            if ($this->request->getParam('create') != null) {
                $page->parentId = $this->request->getParam('create');
            }
        } else {
            
            $pageTitle = Translator::translate('Edit page') . " '$page->title'";
        }

        $model = $this->prepareModel($page, $currentSite);

        $mb = new DefaultModelBinder();
        $mb->bindModel($model);

        if ($this->request->isPost()) {
            if (!empty($model->newCustomField->name)) {
                foreach ($page->customFields as $customField) {
                    if ($customField->name == $model->newCustomField->name) {
                        $mb->getPropertyMetaData('newCustomField-name')
                           ->getValidationResult()
                           ->setValid(false)
                           ->addValidationMessage("There is already a custom field with the name '{$customField->name}'. Please choose another name.");
                        break;
                    }
                }
            }

            if ($mb->isModelValid()) {
                try {

                    if (!empty($model->newCustomField->name)) {
                        $model->page->customFields[] = clone $model->newCustomField;
                        View::getMessenger()->addMessage("Added the custom field '{$model->newCustomField->name}'.");
                        $model->newCustomField = new CustomFieldEntity();
                    }

                    $deleteCustomFieldId = $this->request->getPost('delete-custom-field');
                    if ($deleteCustomFieldId != null) {
                        foreach ($model->page->customFields as $key => $customField) {
                            if ($customField->id == $deleteCustomFieldId) {
                                View::getMessenger()->addMessage("Custom field '{$customField->name}' deleted.");
                                unset($model->page->customFields[$key]);
                                break;
                            }
                        }
                    }

                    if ($model->page->created === null) {
                        $model->page->created = date('Y-m-d H:i:s');
                    }

                    if ($model->page->updated === null) {
                        $model->page->updated = date('Y-m-d H:i:s');
                    }

                    $model->page->siteId = $currentSite->id;

                    $this->pageService->save($model->page);

                    $this->cacheHandler->clean('backend-pages-' . $currentSite->id . $this->applicationContext->getCurrentAdministrator()->id);
                    $this->pageService->purgeCache('page-' . $model->page->id);

                    // FIXME: Rensa all cache för alla sidor där den nuvarande sidan förekommer...
                    $pageTreeMeta = Session::get('__page_tree_meta');
                    if ($pageTreeMeta != null) {
                        if (isset($pageTreeMeta[$model->page->parentId])) {
                            if ($pageTreeMeta[$model->page->parentId]) {
                                $pageTreeMeta[$model->page->id] = true;
                            } else if ($pageTreeMeta[$model->page->parentId] === null) {
                                $pageTreeMeta[$model->page->parentId] = false;
                            }
                        }
                        Session::set('__page_tree_meta', $pageTreeMeta);
                    }

                    View::getMessenger()->addMessage('Saved the page.');
                    $this->request->redirectTo('pages/edit/' . $page->id);
                } catch (Exception $e) {
                    $errorMessage = Translator::translate('Could not save the page: ')
                                  . $e->getMessage();
                    View::getMessenger()
                        ->addMessage($errorMessage, Messenger::ERROR);
                }
            } else {
                View::getMessenger()
                        ->addMessage('The form is not valid.', Messenger::ERROR);
            }
        }

        $this->view->setVar('title', $pageTitle)
                   ->setVar('siteUrl', $this->applicationContext->getCurrentSite()->url)
                   ->setVar('model', $model)
                   ->setName('pages/edit')
                   ->render();
    }

    /**
     *
     * @param PageEntity $page
     * @param SiteEntity $currentSite
     * @return PageViewModel 
     */
    private function prepareModel(PageEntity $page, SiteEntity $currentSite)
    {
        $model = new PageViewModel();
        $model->page = $page;
        $model->newCustomField = new CustomFieldEntity();

        // Add "Parent page" select options.
        $rootPage = $this->pageService->getRootPage($currentSite->id);
        if ($rootPage != null) {
            $this->buildParentPageTree($model, $rootPage, $model->page, 0);
        }

        $templates = $this->templateService->getTemplates($currentSite->theme);
        foreach ($templates as $template) {
            $model->templates[$template->name] = $template->name;
        }

        $model->statuses = array(
            1 => Translator::translate('Hidden'),
            2 => Translator::translate('Preview'),
            3 => Translator::translate('Published')
        );

        $model->sortOrders = array(
            PageEntity::CHILDREN_SORT_ORDER_WEIGHT => Translator::translate('By weight'),
            PageEntity::CHILDREN_SORT_ORDER_ALPHABETIC_ASCENDING => Translator::translate('Alphabetic ascending'),
            PageEntity::CHILDREN_SORT_ORDER_ALPHABETIC_DESCENDING => Translator::translate('Alphabetic descending'),
            PageEntity::CHILDREN_SORT_ORDER_CHRONOLOGIC_ASCENDING => Translator::translate('Chronologic ascending'),
            PageEntity::CHILDREN_SORT_ORDER_CHRONOLOGIC_DESCENDING =>Translator::translate('Chronologic descending')
        );
        
        return $model;
    }

    protected function handleListAction()
    {
        $pageTitle = Translator::translate('Pages');
        
        $siteId = $this->applicationContext->getCurrentSite()->id;
        $pageTree = $this->cacheHandler->load('backend-pages-' . $siteId . Session::getId());
        $pageTreeMeta = Session::get("__page_tree_meta");
        $rootPage = $this->pageService->getRootPage($siteId);
        if ($rootPage != null) {
            $rootPageChildren = $this->pageService->getChildrenFor($rootPage);
            if ($pageTree === null) {
                $pageTree = $rootPage;
                if ($pageTreeMeta === null) {
                    $pageTreeMeta = array();
                    $rootPageHasChildren = ($rootPageChildren != null) ? true : false;
                    $pageTreeMeta[$rootPage->id] = $rootPageHasChildren;
                    if ($rootPageHasChildren) {
                        foreach ($rootPageChildren as $child) {
                            $pageTreeMeta[$child->id] = $this->pageService->hasPageChildren($child->id) ? false : null;
                        }
                    }
                    Session::set('__page_tree_meta', $pageTreeMeta);
                } else {
                    $this->buildPageTree($pageTreeMeta, $rootPage);
                }
                $this->cacheHandler->save('backend-pages-' . $siteId . $this->applicationContext->getCurrentAdministrator()->id, $pageTree);
            }
        }

        $this->view->setVar('title', $pageTitle)
                   ->setVar('rootPage', $pageTree)
                   ->setVar('pageTreeMeta', $pageTreeMeta)
                   ->render();
    }

    private function buildPageTree(array &$pageTreeMeta, PageEntity $page)
    {
        if (isset($pageTreeMeta[$page->id]) && $pageTreeMeta[$page->id] === true) {
            $children = $this->pageService->getChildrenFor($page);
            if ($children == null) {
                $pageTreeMeta[$page->id] = null;
                return;
            }
            foreach ($children as $child) {
                $this->buildPageTree($pageTreeMeta, $child);
            }
        }
    }

    /**
     *
     * @param PageForm $pageViewModel
     * @param array $pages
     * @param PageEntity $currentPage
     */
    private function buildParentPageTree(PageViewModel $pageViewModel, PageEntity $page, PageEntity $currentPage, $level) {
        if ($page->id == $currentPage->id) {
            return;
        }

        $text = str_repeat('- ', $level) . $page->title;

        $pageViewModel->possibleParents[$page->id] = $text;

        $children = $this->pageService->getChildrenFor($page);

        if ($children !== null) {
            $level++;
            foreach ($children as $child) {
                $this->buildParentPageTree($pageViewModel, $child, $currentPage, $level);
            }
        }
    }
}
